#pragma once
#include <string>
#include <vector>

#include "UMergeEngine.h"

#include <libxml/tree.h>
#include <libxml/chvalid.h>
#include <libxml/HTMLparser.h>
#include <libxml/HTMLTree.h>

#define  DIFF_VERSION 2

using namespace std;

class XmlNodeHolder
{
    public:
        XmlNodeHolder(const xmlNodePtr ptr)
        {
            node = ptr;
        }
        ~XmlNodeHolder(void){};

        xmlNodePtr node;
        
        bool operator== (const XmlNodeHolder& ptr)
        {
            xmlChar* content1 = ptr.node->content;
            xmlChar* content2 = node->content;
            int length1 = xmlStrlen(content1)+1;
            int length2 = xmlStrlen(content2)+1;
            int restlen1 = length1;
            int restlen2 = length2;
            int ch1 = 0;
            int ch2 = 0;
            do
            {
                ch1 = getNextSymbol(content1, &length1);
                ch2 = getNextSymbol(content2, &length2);
                if (ch1 == 0 && ch2 == 0)
                    return true;
                if (ch1 == -1 && ch2 == -1)
                    return false;
                if (ch1 != ch2)
                    return false;
                content1 += length1;
                content2 += length2;
                restlen1 -= length1;
                restlen2 -= length2;
                length1 = restlen1;
                length2 = restlen2;
            }
            while (ch1 == ch2);
            return false;
        };
    private:
        inline int getNextSymbol(xmlChar* content, int* len)
        {
            int length = *len;
            *len = 0;
            int restlen = length;
            int ch = xmlGetUTF8Char(content, &length);
            while (ch > 0 && xmlIsCharQ(ch) && (xmlIsBlankQ(ch) || ch == 0xa0))
            {
                *len += length;
                content += length;
                restlen -= length;
                length = restlen;
                ch = xmlGetUTF8Char(content, &length);
            }
            *len += length;
            return ch;
        };
};

class DiffHLEngine
{
public:
    DiffHLEngine(void);
    ~DiffHLEngine(void);

    static unsigned int GetVersion() {return DIFF_VERSION;}

    bool SetCurrentResource(    const string& szResource,
                                const string& szUserCharset,
                                const string& szServerCharset,
                                const string& szEffectiveUrl,
                                unsigned int& uiContentType);

    bool SetPreviousResource(   const string& szResource,
                                const string& szUserCharset,
                                const string& szServerCharset,
                                const string& szEffectiveUrl,
                                unsigned int& uiContentType);

    void Process(   const int& iHLFontColor, const int& iHLBkColor, const unsigned int& uiHLFontStyle,
                    const string& szXPath, const unsigned int& uiTriggerType, const string& szTriggerParams,
                    bool& bDiffCacheInput, bool& bDiffCacheOutput, string& szDiffCache, double& fDiffTotalTime,
                    xmlDocPtr& pDiffHLResult, xmlDocPtr& pChangesSummary,                    
                    unsigned int& uiBlocksAdded, unsigned int& uiBlocksDeleted,
                    unsigned int& uiQtyTriggers, unsigned int& uiQtyAnchors);

    void GetFaviconURLFromResource(string& szFaviconUrl);

    void GetTitleFromResource(string& szTitle);

    bool DumpDiffHLResult(xmlDocPtr pDiffHLResult, string& szDiffHLResult);

    bool DumpChangesSummary(xmlDocPtr pChangesSummary,
                            const int& iHLFontColor, const int& iHLBkColor, const unsigned int& uiHLFontStyle,
                            const unsigned int& uiTriggerType, const string& szTriggerParams,
                            string& szChangesSummary);

    void CreateRssFeed( string& szRssFeed,
                        const string& szFileName,
                        const string& szMonitorTitle,
                        const string& szMonitorUrl,
                        const unsigned int& uiMonitorID,
                        const unsigned int& uiResourceID,
                        const unsigned int& uiQtyTriggers,
                        const string& szChangesSummary);
    
    void CreateAtomFeed(    string& szAtomFeed,
                            const string& szFileName,
                            const string& szMonitorTitle,
                            const string& szMonitorUrl,
                            const unsigned int& uiMonitorID,
                            const unsigned int& uiResourceID,
                            const unsigned int& uiQtyTriggers,
                            const string& szChangesSummary);

private:
    void InternalProcess(   const bool& bHighLightChanges,
                            vector<XmlNodeHolder>& nodes,
                            const bool& bIsFirstDHL,
                            const string& szRegex,
                            const int& iHLFontColor,
                            const int& iHLBkColor,
                            const unsigned int& uiHLFontStyle,
                            xmlDocPtr& pChangesSummary,
                            unsigned int& uiQtyTriggers,
                            unsigned int& uiQtyAnchors);

    void HighLightAnyWordFromList(  vector<XmlNodeHolder>& nodes,
                                    const bool& bIsFirstDHL,
                                    const string& szWordsList,
                                    const int& iHLFontColor,
                                    const int& iHLBkColor,
                                    const unsigned int& uiHLFontStyle,
                                    xmlDocPtr& pChangesSummary,
                                    unsigned int& uiQtyTriggers,
                                    unsigned int& uiQtyAnchors);
    
    
    void HighLightAllWordsFromList( vector<XmlNodeHolder>& nodes,
                                    const bool& bIsFirstDHL,
                                    const string& szWordsList,
                                    const int& iHLFontColor,
                                    const int& iHLBkColor,
                                    const unsigned int& uiHLFontStyle,
                                    xmlDocPtr& pChangesSummary,
                                    unsigned int& uiQtyTriggers,
                                    unsigned int& uiQtyAnchors);
    
    void HighLightPhrase(   vector<XmlNodeHolder>& nodes,
                            const bool& bIsFirstDHL,
                            const string& szPhrase,
                            const int& iHLFontColor,
                            const int& iHLBkColor,
                            const unsigned int& uiHLFontStyle,
                            xmlDocPtr& pChangesSummary,
                            unsigned int& uiQtyTriggers,
                            unsigned int& uiQtyAnchors);
    
    void HighLightRegex(    vector<XmlNodeHolder>& nodes,
                            const bool& bIsFirstDHL,
                            const string& szRegex,
                            const int& iHLFontColor,
                            const int& iHLBkColor,
                            const unsigned int& uiHLFontStyle,
                            xmlDocPtr& pChangesSummary,
                            unsigned int& uiQtyTriggers,
                            unsigned int& uiQtyAnchors);

    void HighLightChangesSummary(   vector<XmlNodeHolder>& nodes,
                                    const int& iHLFontColor, const int& iHLBkColor, const unsigned int& uiHLFontStyle,
                                    const unsigned int& uiTriggerType, const string& szTriggerParams);

    void ProcessXSLT(const string& szXml, string& szHtml, unsigned int& uiDetailedContentType);

    void AddToChangesSummary(xmlNodePtr node, xmlDocPtr& pChangesSummary);

    void InsertW3MLinkNode(xmlNodePtr node, unsigned int& uiCounter);

    void GetParentLinkNode(xmlNodePtr node, xmlNodePtr& linkNode);
    
    void GetFirstNodeByName(xmlNodePtr node, char* name, xmlNodePtr& resultNode);
    
    void GetAllNodesByName(xmlNodePtr node, const string& name, vector<xmlNodePtr>& nodes);
    
    void GetAllTextNodes(xmlNodePtr node, vector<XmlNodeHolder>& nodes);

    void GetAllCDataNodes(xmlNodePtr node, vector<XmlNodeHolder>& nodes);

    void GetNodePath(xmlNodePtr node, vector<xmlNodePtr>& nodes);

    void InsertBaseTag(xmlNodePtr root, const string& szUrl);
    
    void InsertXPathRegionTag(xmlNodePtr root, const int& uiColor);

    void GetContentCharsetFromResource(const string& szResource, string& szCharset);
    
    void CreateHighLightNodes(  const int& uiHLFontColor,
                                const int& uiHLBkColor,
                                const unsigned int& uiHLFontStyle,
                                xmlNodePtr& node, xmlNodePtr& last);

    void DumpHtmlDocToString(xmlDocPtr doc, string& szString);

    void DumpXmlDocToString(xmlDocPtr doc, string& szString);

    void DumpHtmlNodeToString(xmlDocPtr doc, xmlNodePtr node, string& szString);

    void EscapeString(string& szString);

    void MergeResultToString(const MergerResultVector& mergeResult, string& szString);

    void StringToMergeResult(const string& szString, MergerResultVector& mergeResult);

private:
    xmlDocPtr prevHtmlDoc;
    xmlDocPtr curHtmlDoc;
    
    xmlDocPtr changesSummary;
    
    string szPrevEffectiveUrl;
    string szCurEffectiveUrl;
    
    unsigned int uiPrevContentType;
    unsigned int uiCurContentType;

    vector<xmlNodePtr> nodePath;
};
